using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.IO.IsolatedStorage;
using System.Text;
using System.Threading;
using System.Speech.Synthesis;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace CorporationWars
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        public const string savedGamePattern = "*.sav";

        public static UIControlBase DraggingControll = null;
        
        SpeechSynthesizer Speechsynth = new SpeechSynthesizer();        
        
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public GameData GameData = new GameData();

        public UIKeyboard saveKeyboard;
        public ScrollableListBox<SavedFile> loadFilesLst;
        
        public List<string> corpStockNames { get {  return GameData.corpStockNames; } set { GameData.corpStockNames = value; } }
        public Dictionary<string, Corporation> GlobalCorpListByID { get { return GameData.GlobalCorpListByID; } set { GameData.GlobalCorpListByID = value; } }
        public List<Corporation> GlobalCorpList { get { return GameData.GlobalCorpList; } set { GameData.GlobalCorpList = value; } }
        public List<Corporation> GlobalCorpListOrderedBySharePrice = null;

        public Corporation playerCorp { get { return GameData.playerCorp; } set { GameData.playerCorp = value; } }

        public Color HudBGCol { get { return new Color(.125f, .125f, .125f, .5f); } }

        public Camera camera;
        public Vector3 camLastPos = Vector3.Zero;
        public Vector3 CameraStartPos = new Vector3(0, 1, 0);

        List<UIButton> HUDButtons { get { return GameData.HUDButtons; } set { GameData.HUDButtons = value; } }
        List<UIZone> OptionsButtons = new List<UIZone>();
        
        public Point ptrPos;
        public Point lstptrPos;

        Random rnd = new Random(DateTime.Now.Millisecond);

        protected MouseState thisMS;
        protected MouseState lastMS;

        public KeyboardState thisKB;
        public KeyboardState lastKB;

        public TimeSpan CycleStart { get { return GameData.CycleStart; } set { GameData.CycleStart = value; } }
        public TimeSpan CycleSpan { get { return GameData.CycleSpan; } set { GameData.CycleSpan = value; } }
        public TimeSpan NextCycle { get { return GameData.NextCycle; } }

        Texture2D tmpTexture;
        Texture2D RedTexture;

        SoundEffectInstance bgSound;

        public int Year { get { return GameData.Year; } set { GameData.Year = value; } }
        public int Cycle { get { return GameData.Cycle; } set { GameData.Cycle = value; } }
        public CyberSector CurrentSector { get { return GameData.CurrentSector; } set { GameData.CurrentSector = value; } }

        public List<NewsItem> NewsFeed { get { return GameData.NewsFeed; } set { GameData.NewsFeed = value; } }
        public List<NewsItem> NewsArchive { get { return GameData.NewsArchive; } set { GameData.NewsArchive = value; } }

        public List<SoundEffectInstance> suroundSounds = new List<SoundEffectInstance>();

        public Dictionary<string, CyberSector> Sectors { get { return GameData.Sectors; } set { GameData.Sectors = value; } }

        public UIZone IsMouseOverUIZone = null;
        public List<decimal> CycleMarketValue { get { return GameData.CycleMarketValue; } set { GameData.CycleMarketValue = value; } }

        public decimal StockCommisionPercentage { get { return GameData.StockCommisionPercentage; } set { GameData.StockCommisionPercentage = value; } }

        CPU2DFire Fire2;

        public RenderTarget2D Scene;
        public RenderTarget2D fireEffect;

        AudioListener al;
        Dictionary<SoundEffectInstance, AudioEmitter> AudioEmiters = new Dictionary<SoundEffectInstance, AudioEmitter>();
        public bool TutorialOn = true;
        public bool AudioOn = true;
        public bool SpeechOn = true;
        public bool MusicOn = true;
        public bool SFXOn = true;
        public float MusicVolume = 1;

        List<UIPopupMessageBox> PopUps = new List<UIPopupMessageBox>();

        SoundEffectInstance Music;

        public bool OptionsScreen = false;

        public Dictionary<Color, Color[]> ColorArray = new Dictionary<Color, Color[]>();

        bool ShowTutorial = false;
        public Game1()
        {
            LoadOptions();

            ShowTutorial = TutorialOn;

            Fire2 = new CPU2DFire(this, new Point(400, 240));
            Fire2.Visible = false;
            Components.Add(Fire2);

            graphics = new GraphicsDeviceManager(this);
            //IsMouseVisible = true;


            graphics.PreferredBackBufferWidth = 960;
            graphics.PreferredBackBufferHeight = 600;
            Content.RootDirectory = "Content";

            camera = new Camera(this, .1f, 20000);

            playerCorp = new Corporation(this,"Test Corp") {Position = new Vector3(0, Corporation.Floor, -5), Description = "This is your corporation..." };
            //playerCorp.fwd = new FireWallData("playerWall", playerCorp.Position, new Vector3(1.5f, 1.5f, 1), Color.White);

            // Create Sectors..
            Sectors.Add("Novo Solo", new CyberSector(this, "Novo Solo", playerCorp) { Exits = new List<string>(new string[] { "St Ives Lobos", "Chiba Central", "Monterrey Junction", "Shan Feng" }) });
            Sectors.Add("St Ives Lobos", new CyberSector(this, "St Ives Lobos") { Exits = new List<string>(new string[] { "Novo Solo", "Chiba Central", "Monterrey Junction", "Shan Feng" }) });
            Sectors.Add("Chiba Central", new CyberSector(this, "Chiba Central") { Exits = new List<string>(new string[] { "St Ives Lobos", "Novo Solo", "Monterrey Junction", "Shan Feng" }) });
            Sectors.Add("Monterrey Junction", new CyberSector(this, "Monterrey Junction") { Exits = new List<string>(new string[] { "St Ives Lobos", "Chiba Central", "Novo Solo", "Shan Feng" }) });
            Sectors.Add("Shan Feng", new CyberSector(this, "Shan Feng") { Exits = new List<string>(new string[] { "St Ives Lobos", "Chiba Central", "Monterrey Junction", "Novo Solo" }) });

            SetCurrentSector("Novo Solo");

            camera.Position = CameraStartPos;  
          
            // Generate Color Array
            GenerateColorArray(Color.Green);
            GenerateColorArray(Color.SteelBlue);            
        }

        public void GenerateColorArray(Color color, float AlphaStart = .125f)
        {
            Color[] colors = new Color[6];
            float div = 2;
            byte d = 3;
            colors[0] = new Color(color.R / d, color.G / d, color.B / d, ((byte)(AlphaStart*255)) / d);
            for (int c = 1; c < colors.Length; c++)
            {
                d = (byte)((c + 1) * div);
                colors[c] = new Color(colors[0].R / d, colors[0].G / d, colors[0].B / d, colors[0].A / d);
            }

            ColorArray.Add(color, colors);
        }

        public void drawLine(Vector2 v1, Vector2 v2, int lineWidth, Color color, Vector2 shadowOffset = new Vector2())
        {
            float angle = (float)Math.Atan2(v2.Y - v1.Y, v2.X - v1.X);
            float length = Vector2.Distance(v1, v2);

            spriteBatch.Draw(Content.Load<Texture2D>("Textures/white"), v1 + shadowOffset, null, Color.Black,
                                   angle, Vector2.Zero, new Vector2(length, lineWidth),
                                   SpriteEffects.None, 0);

            spriteBatch.Draw(Content.Load<Texture2D>("Textures/white"), v1, null, color,
                                   angle, Vector2.Zero, new Vector2(length, lineWidth),
                                   SpriteEffects.None, 0);
        }

        public void RegisterCorp(Corporation corp)
        {
            if (!GlobalCorpListByID.ContainsKey(corp.ID))
            {
                GlobalCorpListByID.Add(corp.ID, corp);
                GlobalCorpList.Add(corp);
            }
        }

        public bool RegisterStockName(string name)
        {
            if (corpStockNames.Contains(name))
                return false;
            else
                corpStockNames.Add(name);

            return true;
        }

        public string lastSector;
        public void SetCurrentSector(string name)
        {
            if (CurrentSector != null)
                lastSector = CurrentSector.Name;
            else
                lastSector = name;

            CurrentSector = Sectors[name];
            CurrentSector.GotoExit(lastSector);

            if(lastSector != CurrentSector.Name)
                Say(string.Format("Good bye {0}, hello {1}...", lastSector, CurrentSector.Name));
            else
                Say(string.Format("Hello {0}...", CurrentSector.Name));
        }

        public CyberSector GetSector(string name)
        {
            return Sectors[name];
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Services.AddService(spriteBatch.GetType(), spriteBatch);

            // TODO: use this.Content to load your game content here
            // Set up RT's
            Scene = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
            fireEffect = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color, DepthFormat.None);

            tmpTexture = new Texture2D(GraphicsDevice, 1, 1);
            tmpTexture.SetData<Color>(new Color[] { HudBGCol });

            RedTexture = new Texture2D(GraphicsDevice, 1, 1);
            RedTexture.SetData<Color>(new Color[] { Color.DarkRed });

            newsPos = new Vector2(GraphicsDevice.Viewport.Width, 0);

            float ctr = (GraphicsDevice.Viewport.Width / 2);
            float btm = GraphicsDevice.Viewport.Height;

            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Exit", Position = new Vector2(0, (int)btm - 64), Width = 64, Height = 64, Color = Color.Aquamarine, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "Exit", OnClick = ButtonClicked, IsVisible = false, ToolTipText = "Exit" });
            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Finance", Position = new Vector2((int)ctr - 160, (int)btm - 64), Width = 64, Height = 64, Color = Color.LightGreen, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "Finance", OnClick = ButtonClicked, ToolTipText = "Finance" });
            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/HR", Position = new Vector2((int)ctr - 96, (int)btm - 64), Width = 64, Height = 64, Color = Color.Orange, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "HR", OnClick = ButtonClicked, ToolTipText = "Human Resource" });
            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Home", Position = new Vector2((int)ctr - 32, (int)btm - 64), Width = 64, Height = 64, Color = Color.Silver, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "Home", OnClick = ButtonClicked, ToolTipText = "Go Home" });
            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Admin", Position = new Vector2((int)ctr + 32, (int)btm - 64), Width = 64, Height = 64, Color = Color.CornflowerBlue, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "Admin", OnClick = ButtonClicked, ToolTipText = "Administration" });
            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/IT", Position = new Vector2((int)ctr + 96, (int)btm - 64), Width = 64, Height = 64, Color = Color.LawnGreen, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "IT", OnClick = ButtonClicked, ToolTipText = "Information Technology" });
            if(AudioOn)
                HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/AudioOn", Position = new Vector2(GraphicsDevice.Viewport.Width - 128, (int)btm - 64), Width = 64, Height = 64, Color = Color.LightGreen, HoverColor = Color.Gold, MouseDownColor = Color.Red, Name = "Audio", OnClick = ButtonClicked, ToolTipText = "Sound On/Off" });
            else
                HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/AudioOn", Position = new Vector2(GraphicsDevice.Viewport.Width - 128, (int)btm - 64), Width = 64, Height = 64, Color = Color.Red, HoverColor = Color.Gold, MouseDownColor = Color.Red, Name = "Audio", OnClick = ButtonClicked, ToolTipText = "All Sound On/Off" });

            HUDButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Options", Position = new Vector2(GraphicsDevice.Viewport.Width - 64, (int)btm - 64), Width = 64, Height = 64, Color = Color.White, HoverColor = Color.Gold, MouseDownColor = Color.Red, Name = "Options", OnClick = ButtonClicked, ToolTipText = "Options" });

            OptionsButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/Exit", Position = new Vector2(0, (int)btm - 64), Width = 64, Height = 64, Color = Color.Aquamarine, HoverColor = Color.White, MouseDownColor = Color.Red, Name = "Exit", OnClick = OptionsButtonClicked, ToolTipText = "Exit" });
            OptionsButtons.Add(new UICheckBox(this) { Text = "Tutorial Mode On/Off", Position = new Vector2(128, 288), Size = 32, Color = Color.White, Value = TutorialOn, ToolTipText = "Turn Tutorial Mode on/off", OnClick = OptionsButtonClicked, Name = "Tutorial" });
            OptionsButtons.Add(new UICheckBox(this) { Text = "Speech On/Off", Position = new Vector2(128, 248), Size = 32, Color = Color.White, Value = SpeechOn, ToolTipText = "Turn Speech on/off", OnClick = OptionsButtonClicked, Name = "Speech" });
            OptionsButtons.Add(new UICheckBox(this) { Text = "SFX On/Off", Position = new Vector2(128, 208), Size = 32, Color = Color.White, Value = SFXOn, ToolTipText = "Turn Sound Effects on/off", OnClick = OptionsButtonClicked, Name = "SFX" });
            OptionsButtons.Add(new UICheckBox(this) { Text = "Music On/Off", Position = new Vector2(128, 168), Size = 32, Color = Color.White, Value = MusicOn, ToolTipText = "Turn Music on/off", OnClick = OptionsButtonClicked, Name = "Music" });
            OptionsButtons.Add(new UICheckBox(this) { Text = "Audio On/Off", Position = new Vector2(128, 128), Size = 32, Color = Color.White, Value = AudioOn, ToolTipText = "Turn ALL sound on/off", OnClick = OptionsButtonClicked, Name = "Audio" });

            OptionsButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/SaveButton", Position = new Vector2((GraphicsDevice.Viewport.Width / 2) + 148, 128), Width = 128, Height = 64, Color = Color.White, HoverColor = Color.LimeGreen, MouseDownColor = Color.Red, Name = "Save", OnClick = OptionsButtonClicked, ToolTipText = "Save current game", HasShadowOnUp = true });
            OptionsButtons.Add(new UIButton(this) { TextureAsset = "Textures/HUD/Buttons/LoadButton", Position = new Vector2((GraphicsDevice.Viewport.Width / 2), 128), Width = 128, Height = 64, Color = Color.White, HoverColor = Color.LimeGreen, MouseDownColor = Color.Red, Name = "Load", OnClick = OptionsButtonClicked, ToolTipText = "Load a saved game", HasShadowOnUp = true });            

            saveKeyboard = new UIKeyboard(this) { IsVisible = false, KeyColor = Color.SteelBlue, KeyHoverColor = Color.White, KeyMouseDownColor = Color.DarkSlateBlue, KeyShadowHeight = 4, Position = new Vector2((GraphicsDevice.Viewport.Width / 2) - 92, 216), OnReturn = LoadSaveKeyboardReturn, Title = "Enter File Name To Save:-" };

            loadFilesLst = new ScrollableListBox<SavedFile>(this, "DisplayName", new Vector2((GraphicsDevice.Viewport.Width / 2) - 92, 216), 400, 108, "Fonts/Font") { IsVisible = false, IsDragable = false };
            loadFilesLst.OnOK = SelectedLoadGame;
            
            al = new AudioListener();

            suroundSounds.Add(Content.Load<SoundEffect>("Audio/Music/FunkyDrummer135").CreateInstance());
            suroundSounds.Add(Content.Load<SoundEffect>("Audio/Music/Gix135").CreateInstance());
            suroundSounds.Add(Content.Load<SoundEffect>("Audio/Music/HipHop135").CreateInstance());
            suroundSounds.Add(Content.Load<SoundEffect>("Audio/Music/Jungle135").CreateInstance());

            bgSound = Content.Load<SoundEffect>("Audio/Music/BGLoop").CreateInstance();
            bgSound.IsLooped = true;
            bgSound.Volume = MusicVolume/2f;
            //if(AudioOn)
            //    bgSound.Play();

            Music = Content.Load<SoundEffect>("Audio/Music/Yeah Zep26nov06").CreateInstance();
            Music.IsLooped = true;
            Music.Volume = MusicVolume;
            if (AudioOn && MusicOn)
                Music.Play();

            AudioEmiters = new Dictionary<SoundEffectInstance,AudioEmitter>();
            int ae = 0;
            foreach (SoundEffectInstance ss in suroundSounds)
            {
                ss.IsLooped = true;
                ss.Volume = MusicVolume;

                switch (ae)
                {
                    case 0:
                        AudioEmiters.Add(ss, new AudioEmitter() { Position = new Vector3(Corporation.SpaceSize, Corporation.SpaceSize / 2, Corporation.SpaceSize), DopplerScale = .2f });
                        break;
                    case 1:
                        AudioEmiters.Add(ss, new AudioEmitter() { Position = new Vector3(Corporation.SpaceSize, Corporation.SpaceSize / 2, -Corporation.SpaceSize), DopplerScale = .2f });
                        break;
                    case 2:
                        AudioEmiters.Add(ss, new AudioEmitter() { Position = new Vector3(-Corporation.SpaceSize, Corporation.SpaceSize / 2, Corporation.SpaceSize), DopplerScale = .2f });
                        break;
                    case 3:
                        AudioEmiters.Add(ss, new AudioEmitter() { Position = new Vector3(-Corporation.SpaceSize, Corporation.SpaceSize / 2, -Corporation.SpaceSize), DopplerScale = .2f });
                        break;
                }
                ae++;
                
                ss.Apply3D(al, AudioEmiters[ss]);
                if (AudioOn && SFXOn)
                    ss.Play();
            }
        }

        
        public void SwitchAudio()
        {
            AudioOn = !AudioOn;
            SetMusic();
            SetSFX();


            HUDButtons.SingleOrDefault(entity => entity.Name == "Audio").Color = AudioOn ? Color.LightGreen : Color.Red;
            ((UICheckBox)OptionsButtons.SingleOrDefault<UIZone>(entity => entity.Name == "Audio")).Value = AudioOn;

            if (AudioOn)
                Say("Audio is on");
        }

        public void SwitchMusic()
        {
            MusicOn = !MusicOn;
            SetMusic();


            ((UICheckBox)OptionsButtons.SingleOrDefault<UIZone>(entity => entity.Name == "Music")).Value = MusicOn;

            if (MusicOn)
                Say("Music is on");
            else
                Say("Music is off");
        }

        public void SetMusic()
        {
            if (MusicOn && AudioOn)
            {
                if (Music.State != SoundState.Paused)
                    Music.Play();
                else
                    Music.Resume();

                //if (bgSound.State == SoundState.Stopped)
                //    bgSound.Play();
                //else
                //    bgSound.Resume();
            }
            else
            {
                Music.Pause();
                
                //bgSound.Pause();
            }
        }

        public void SwitchSFX()
        {
            SFXOn = !SFXOn;

            SetSFX();

            ((UICheckBox)OptionsButtons.SingleOrDefault<UIZone>(entity => entity.Name == "SFX")).Value = SFXOn;

            if (SFXOn)
                Say("Sound effects are on");
            else
                Say("Sound effects are off");
        }

        public void SwitchSpeech()
        {
            SpeechOn = !SpeechOn;
            ((UICheckBox)OptionsButtons.SingleOrDefault<UIZone>(entity => entity.Name == "Speech")).Value = SpeechOn;

            if (SpeechOn)
                Say("Speech is on");
        }

        public void SwitchTutorial()
        {
            TutorialOn = !TutorialOn;
            ((UICheckBox)OptionsButtons.SingleOrDefault<UIZone>(entity => entity.Name == "Tutorial")).Value = TutorialOn;

            if(TutorialOn)
                Say("Tutorial Mode is on");
            else
                Say("Tutorial Mode is off");
        }

        public void SetSFX()
        {
            if (SFXOn && AudioOn)
            {
                if (v92Instance != null && v92Instance.State == SoundState.Paused)
                    v92Instance.Resume();

                if (v92Instance != null && v92Instance.State == SoundState.Stopped)
                    v92Instance.Play();

                foreach (SoundEffectInstance ss in suroundSounds)
                    ss.Resume();
            }
            else
            {
                if (v92Instance != null && v92Instance.State == SoundState.Playing)
                    v92Instance.Pause();

                foreach (SoundEffectInstance ss in suroundSounds)
                    ss.Pause();
            }
        }

        void OptionsButtonClicked(UIZone sender)
        {
            switch (sender.Name)
            {
                case "Exit":
                    OptionsScreen = false;
                    SaveOptions();
                    break;
                case "Audio":
                    SwitchAudio();                    
                    break;
                case "Music":
                    SwitchMusic();
                    break;
                case "SFX":
                    SwitchSFX();
                    break;
                case "Speech":
                    SwitchSpeech();
                    break;
                case "Tutorial":
                    SwitchTutorial();
                    break;
                case "Save":
                    saveKeyboard.IsVisible = true;
                    Say("Enter the name of the file to save this game too.");
                    break;
                case "Load":
                    loadAGame = true;
                    Say("Select one of the games listed to load, then click the tick.");
                    // Load the saved Files list
                    GetSavedFiles();
                    break;
            }
        }

        void GetSavedFiles()
        {
            IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
            string[] files = isf.GetFileNames(savedGamePattern);

            SavedFiles.Clear();
            foreach (string file in files)
            {
                SavedFiles.Add(new SavedFile() { Name = file, Date = string.Format("{0:dd/MM/yyyy}", isf.GetCreationTime(file)) });
            }

            loadFilesLst.ReSet();
            loadFilesLst.Items = SavedFiles;
            loadFilesLst.IsVisible = true;

        }
        public class SavedFile
        {
            public string Name { get; set; }
            public string Date { get; set; }
            public string Size { get; set; }
            public string DisplayName
            {
                get
                {
                    return string.Format("{0,-18} {1} {2}", Name, Date, Size);
                }
            }
        }
        List<SavedFile> SavedFiles = new List<SavedFile>();

        bool loadAGame = false;

        void GotoPlayerNode(string name)
        {
            if (CurrentSector.Name != playerCorp.SectorBase)
                SetCurrentSector(playerCorp.SectorBase);

            if (CurrentSector.inSideCorp != playerCorp)
                CurrentSector.EnterCorp(playerCorp);

            playerCorp.SetSelectedNode(name);
        }
        void ButtonClicked(UIZone sender)
        {
            switch (sender.Name)
            {
                case "Audio":
                    SwitchAudio();
                    break;
                case "Exit":
                    if (CurrentSector.inSideCorp != null)
                        CurrentSector.LeaveCorp();
                    else
                        Exit();
                    break;
                case "Finance":
                    GotoPlayerNode("Financial");
                    break;
                case "HR":
                    GotoPlayerNode("Human Resources");
                    break;
                case "Home":
                    GotoPlayerNode("Core");                    
                    break;
                case "Admin":
                    GotoPlayerNode("Administration");
                    break;
                case "IT":
                    GotoPlayerNode("Information Technology");
                    break;
                case "Options":
                    OptionsScreen = true;
                    break;

            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        List<int> thisLoop = new List<int>();
        TimeSpan loopTime = TimeSpan.Zero;
        TimeSpan loopStart = TimeSpan.Zero;
        TimeSpan loopPause = TimeSpan.Zero;
        TimeSpan loopPauseStart = TimeSpan.Zero;
        int loopPos = 0;
        public void PlayMusic()
        {
            TimeSpan ts = DateTime.Now.TimeOfDay - loopStart;
            TimeSpan pts = DateTime.Now.TimeOfDay - loopPauseStart;

            if (ts > loopTime && SFXOn)
            {
                if (thisLoop.Count > 0)
                {
                    string snd = "";

                    switch (thisLoop[loopPos])
                    {
                        case 10:
                            snd = "hash";
                            break;
                        case 11:
                            snd = "star";
                            break;
                        case 12:
                            snd = "PAUSE";
                            break;
                        default:
                            snd = thisLoop[loopPos].ToString();
                            break;
                    }
                    if (snd != "PAUSE")
                    {
                        if(AudioOn && SFXOn)
                            Content.Load<SoundEffect>(string.Format("Audio/SFX/dtmf-{0}", snd)).Play(.01f, 0, 0);

                        loopPos++;

                        int ms = (int)MathHelper.Lerp(150, 250, (float)rnd.NextDouble());
                        loopTime = new TimeSpan(0, 0, 0, 0, ms);

                        loopStart = DateTime.Now.TimeOfDay;

                        
                    }
                    else
                    {
                        if (pts > loopPause)
                        {
                            loopPause = TimeSpan.Zero;
                            loopPos++;
                        }

                        if (loopPause == TimeSpan.Zero)
                        {
                            loopPauseStart = DateTime.Now.TimeOfDay;
                            loopPause = new TimeSpan(0, 0, 0, 0, (int)MathHelper.Lerp(500, 5000, (float)rnd.NextDouble()));
                        }
                    }

                    if (loopPos > thisLoop.Count - 1)
                    {
                        loopTime = TimeSpan.Zero;
                        if (v92Instance == null)
                        {
                            v92Instance = Content.Load<SoundEffect>("Audio/SFX/v92").CreateInstance();
                            v92Instance.Volume = .01f;
                        }

                        if (v92Instance.State != SoundState.Playing)
                        {
                            if(AudioOn && SFXOn)
                                v92Instance.Play();
                        }
                    }
                }
            }
            
            if (loopTime == TimeSpan.Zero)
            {
                // gen loop.
                thisLoop.Clear();
                int lSize = (int)MathHelper.Lerp(4, 16, (float)rnd.NextDouble());
                for (int n = 0; n < lSize; n++)
                    thisLoop.Add(rnd.Next(0,13));

                loopPos = 0;
            }
        }

        SoundEffectInstance v92Instance { get; set; }

        public int ScrollWheelValue { get; set; }

        public bool IsKeyPressed(Keys key)
        {
            return thisKB.IsKeyUp(key) && lastKB.IsKeyDown(key);
        }

        public Keys[] PressedKeys()
        {
            return thisKB.GetPressedKeys();
        }

        public Keys[] LastPressedKeys()
        {
            return lastKB.GetPressedKeys();
        }

        long bigmem = 0;
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            mem = GC.GetTotalMemory(false);
            if (mem > bigmem)
                bigmem = mem;

            Window.Title = string.Format("{0} - {1} : {2}", bigmem, mem,mem.ToString().Length);

            if (ShowTutorial)
            {
                ShowTutorial = false;
                ShowPopUpMessage("Tutorial Mode", "You are currently in tutorial mode, go to options to switch this off.", 500, 100, Color.WhiteSmoke, Color.Navy, Color.White, .05f, 10);
            }
            
            PlayMusic();
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            if (IsActive)
            {
                thisMS = Mouse.GetState();
                thisKB = Keyboard.GetState();
            }

            ptrPos = new Point((int)MathHelper.Clamp(thisMS.X, 0, GraphicsDevice.Viewport.Width), (int)MathHelper.Clamp(thisMS.Y, 0, GraphicsDevice.Viewport.Height));

            ScrollWheelValue = thisMS.ScrollWheelValue - lastMS.ScrollWheelValue;

            IsMouseOverUIZone = null;

            if (!OptionsScreen)
            {
                if (CurrentSector.inSideCorp != null)
                    HUDButtons[0].IsVisible = true;
                else
                    HUDButtons[0].IsVisible = false;

                #region Cycle End
                if (CycleStart == TimeSpan.Zero || NextCycle < TimeSpan.Zero)
                {
                    CycleMarketValue.Clear();
                    
                    CycleStart = DateTime.Now.TimeOfDay;
                    string thisCycleDate = getCyberDate();

                    Cycle++;

                    foreach (CyberSector sector in Sectors.Values)
                        sector.EndCycle(thisCycleDate);

                    CycleMarketValue.Add(GlobalCorpList.Average(entity => entity.ThisCycleSharePriceHistory.Average()));

                    if (Cycle > 12)
                    {
                        Year++;
                        Cycle = 1;
                    }

                    NewsArchive.AddRange(NewsFeed);
                    NewsFeed.Clear();

                    // Build News Feed.
                    int nitems = rnd.Next(5, 15);
                    for (int n = 0; n < nitems; n++)
                    {
                        NewsFeed.Add(NewsFactory.GenerateNewsItem(this));
                    }

                    GlobalCorpListOrderedBySharePrice = new List<Corporation>(GlobalCorpList.OrderBy(entity => entity.CurrentBuySharePrice));

                    string sth = string.Empty;
                    switch (Cycle)
                    {
                        case 1:
                            sth = "st";
                            break;
                        case 2:
                            sth = "nd";
                            break;
                        case 3:
                            sth = "rd";
                            break;
                        case 4:
                        case 5:
                        case 6:
                        case 7:
                        case 8:
                        case 9:
                        case 10:
                        case 11:
                        case 12:
                            sth = "th";
                            break;

                    }
                    Say(string.Format("New Cycle! Welcome to the {0}{2} cycle of {1}", Cycle, Year, sth));
                }
                #endregion

                int hbcnt = HUDButtons.Count - 1;
                for(;hbcnt >=0;hbcnt--)
                    HUDButtons[hbcnt].Update(gameTime);

                CurrentSector.Update(gameTime);

                al.Position = camera.Position;
                foreach (SoundEffectInstance ss in suroundSounds)
                    ss.Apply3D(al, AudioEmiters[ss]);

                Corporation.RecordSharePrice = false;

                if (GameData.elapsedRecordTime < TimeSpan.Zero)
                {
                    GameData.lastRecordedSharePrice = DateTime.Now.TimeOfDay;
                    Corporation.RecordSharePrice = true;
                }

                foreach (CyberSector sector in Sectors.Values)
                    sector.UpdateCycle(gameTime);

                if (Corporation.RecordSharePrice)
                    CycleMarketValue.Add(GlobalCorpList.Average(entity => entity.ThisCycleSharePriceHistory.Average()));

                Corporation.RecordSharePrice = false;
            }
            else
            {
                if (!loading && !saving)
                {
                    foreach (UIZone button in OptionsButtons)
                    {
                        if (button is UIButton)
                            ((UIButton)button).Update(gameTime);

                        if (button is UICheckBox)
                            ((UICheckBox)button).Update(gameTime);
                    }

                    saveKeyboard.Update(gameTime);
                    loadFilesLst.Update(gameTime,ScrollWheelValue * .05f);
                }
            }

            base.Update(gameTime);

            if (IsActive)
            {
                lastMS = thisMS;
                lastKB = thisKB;

                lstptrPos = ptrPos;
            }
        }

        string loadSaveFileName = string.Empty;
        public void LoadSaveKeyboardReturn(UIZone sender)
        {
            loadSaveFileName = saveKeyboard.Value;
            saveKeyboard.IsVisible = false;

            if (!string.IsNullOrWhiteSpace(loadSaveFileName))
                SaveGame(string.Format("{0}{1}", loadSaveFileName,savedGamePattern.Replace("*","")));
            else
                ShowErrorMessage("Bad File Name", "You entered a bad file name, please try again.", 300, 100);

        }

        public bool LeftButtonPressed { get { return thisMS.LeftButton == ButtonState.Released && lastMS.LeftButton == ButtonState.Pressed; } }
        public bool LeftButtonDown { get { return thisMS.LeftButton == ButtonState.Pressed; } }
        public bool LeftButtonHeldDown { get { return thisMS.LeftButton == ButtonState.Pressed && lastMS.LeftButton == ButtonState.Pressed; } }

        public bool IsMouseOverCorporation(Corporation corporation)
        {
            BoundingBox box = corporation.Bounds;
            return IsMouseoverBox(box);
        }

        public bool IsMouseOverRectangle(Rectangle rect)
        {
            if (DraggingControll != null)
                return false;

            if (ptrPos.X > rect.Left && ptrPos.X < rect.Right && ptrPos.Y > rect.Top && ptrPos.Y < rect.Bottom)
                return true;
            else
                return false;
        }

        public bool IsMouseOverSolidPixel(Texture2D texture,Vector2 pos, int width, int height)
        {
            Color[] colArray = new Color[texture.Width * texture.Height];
            texture.GetData<Color>(colArray);

            int TexX = ptrPos.X - (int)pos.X;
            int TexY = ptrPos.Y - (int)pos.Y;

            int xRatio = texture.Width / width;
            int yRatio = texture.Height / height;

            TexX *= xRatio;
            TexY *= yRatio;

            float a = colArray[TexX + TexY * texture.Width].A;

            return a/255f == 1;
        }
        public bool IsMouseOverSemiSolidPixel(Texture2D texture, Vector2 pos, int width, int height,Color[] colArray, float tolerance = 1)
        {
            int TexX = ptrPos.X - (int)pos.X;
            int TexY = ptrPos.Y - (int)pos.Y;

            int xRatio = texture.Width / width;
            int yRatio = texture.Height / height;

            TexX *= xRatio;
            TexY *= yRatio;

            float a = colArray[TexX + TexY * texture.Width].A;

            return a / 255f >= tolerance;
        }

        public bool IsMouseoverBox(BoundingBox box)
        {
            if (IsMouseOverUIZone == null && RayPicking(new Point((int)ptrPos.X, (int)ptrPos.Y), box, camera) != float.MaxValue)
                return true;
            else
                return false;
        }

        public bool IsMouseClickedOnBox(BoundingBox box)
        {
            return LeftButtonPressed && IsMouseoverBox(box);
        }

        public int RenderText(string text, Vector2 pos, Color color, int maxWidth = -1, string fontAsset = "Fonts/Font")
        {
            int ls = Content.Load<SpriteFont>(fontAsset).LineSpacing;

            if (maxWidth != -1 && Content.Load<SpriteFont>(fontAsset).MeasureString(text).X > maxWidth)
            {
                string[] splitString = text.Split(new char[] { ' ' });
                string line = string.Empty;
                int lcnt = 0;
                for (int c = 0; c < splitString.Length; c++)
                {
                    string mod = " " + splitString[c].ToString();

                    if (string.IsNullOrEmpty(line))
                        mod = mod.TrimStart(new char[] { ' ' });

                    if (Content.Load<SpriteFont>(fontAsset).MeasureString(line + mod).X > maxWidth)
                    {                        
                        // Write it and clear it.
                        if (lcnt == 0)
                            RenderText(line, pos, color, -1, fontAsset);
                        else
                            RenderText(line, pos + new Vector2(0, ls * lcnt), color, -1, fontAsset);

                        lcnt++;
                        line = string.Empty;
                        mod = mod.TrimStart(new char[] { ' ' });
                    }
                    
                    line += mod;
                }

                if(line != string.Empty)
                    RenderText(line, pos + new Vector2(0, ls * lcnt), color, -1, fontAsset);

                return ls * (lcnt + 1);
            }
            else
            {
                spriteBatch.DrawString(Content.Load<SpriteFont>(fontAsset), text, pos - new Vector2(1, -1), new Color(0, 0, 0, color.A));
                spriteBatch.DrawString(Content.Load<SpriteFont>(fontAsset), text, pos, color);

                return ls;
            }
            
        }

        public int TextLineSpacing { get { return Content.Load<SpriteFont>("Fonts/Font").LineSpacing; } }



        Effect effect;
        float loadRot = 0;

        //public void DrawKeyboard(Vector2 topLeft, Color color, int size, int spacing=8, int shadowHeight = 4)
        //{
        //    int lineNo = 0;
        //    foreach (string line in keyboardLayout)
        //    {
        //        string[] chars = line.Split(new char[] { ',' });
        //        int charNo = 0;
        //        foreach (string ch in chars)
        //        {
        //            //spriteBatch.Draw(Content.Load<Texture2D>("Textures/halfshade"), new Rectangle((int)topLeft.X + ((size + spacing) * charNo), (int)topLeft.Y + ((size + spacing) * lineNo), size + spacing, size + spacing), Color.White);
        //            if (ch != " ")
        //            {
        //                spriteBatch.Draw(Content.Load<Texture2D>("Textures/halfshade"), new Rectangle((int)topLeft.X + ((size + spacing) * charNo), (int)topLeft.Y + ((size + spacing) * lineNo), size + spacing, size + spacing), Color.White);
        //                spriteBatch.Draw(Content.Load<Texture2D>(string.Format("Textures/TypeFont/{0}", ch)), new Rectangle((int)topLeft.X + spacing + ((size + spacing) * charNo) - shadowHeight, (int)topLeft.Y + spacing + ((size + spacing) * lineNo) + shadowHeight, size / 2, size / 2), new Color(0, 0, 0, .5f));
        //                spriteBatch.Draw(Content.Load<Texture2D>(string.Format("Textures/TypeFont/{0}", ch)), new Rectangle((int)topLeft.X + spacing + ((size + spacing) * charNo), (int)topLeft.Y + spacing + ((size + spacing) * lineNo), size / 2, size / 2), color);
        //            }
        //            //else
        //            //    spriteBatch.Draw(Content.Load<Texture2D>("Textures/halfshade"), new Rectangle((int)topLeft.X + ((size + spacing) * charNo), (int)topLeft.Y + ((size + spacing) * lineNo), size + spacing, size + spacing), Color.White);

        //            charNo++;
        //        }
        //        lineNo++;
        //    }
        //}

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            if (OptionsScreen)
            {
                GraphicsDevice.Clear(Color.Black);
                spriteBatch.Begin();

                spriteBatch.Draw(Content.Load<Texture2D>("Textures/Backgrounds/CircuitBoard1"), GraphicsDevice.Viewport.TitleSafeArea, Color.White);
                spriteBatch.Draw(Content.Load<Texture2D>("Textures/HalfShade"), GraphicsDevice.Viewport.TitleSafeArea, Color.White);

                // Render options
                foreach (UIZone button in OptionsButtons)
                {
                    if (button is UIButton)
                        ((UIButton)button).Draw(gameTime);
                    if (button is UICheckBox)
                        ((UICheckBox)button).Draw(gameTime);
                }

                // Draw File List
                if (loadAGame)
                    loadFilesLst.Draw(gameTime);

                if (saveKeyboard != null)
                    saveKeyboard.Draw(gameTime);
                
                spriteBatch.End();
            }
            else
            {
                // Set up Scene RT
                GraphicsDevice.SetRenderTarget(Scene);
                GraphicsDevice.Clear(Color.Black);

                // TODO: Add your drawing code here

                //base.Draw(gameTime);
                spriteBatch.Begin();

                CurrentSector.Draw(gameTime);

                // News
                if (NewsFeed.Count == 0)
                {
                    // Load with random news items
                    NewsFeed.Add(new NewsItem() { HeadLine = "Cat Lost!", TagLine = "Mrs V.Smith has lost her cat..." });
                }

                spriteBatch.Draw(tmpTexture, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, TextLineSpacing), Color.White);
                string news = string.Empty;
                foreach (NewsItem newsItem in NewsFeed)
                {
                    news += string.Format("[{0}] - {1}    ", newsItem.HeadLine, newsItem.TagLine);
                }
                string n = "News:-";

                newsPos -= new Vector2(1f, 0);
                float newsLen = Content.Load<SpriteFont>("Fonts/Font").MeasureString(news).X;
                RenderText(news, newsPos, Color.Silver);

                spriteBatch.Draw(RedTexture, new Rectangle(0, 0, (int)Content.Load<SpriteFont>("Fonts/Font").MeasureString(n).X + 8, TextLineSpacing), Color.White);

                if (newsPos.X < -newsLen)
                    newsPos = new Vector2(GraphicsDevice.Viewport.Width, 0);

                RenderText(n, new Vector2(4, 0), Color.Gold);
                // HUD
                spriteBatch.Draw(tmpTexture, new Rectangle(0, GraphicsDevice.Viewport.Height - 25, GraphicsDevice.Viewport.Width, 25), Color.White);
                spriteBatch.Draw(tmpTexture, new Rectangle(0, TextLineSpacing, GraphicsDevice.Viewport.Width, TextLineSpacing), Color.White);
                string str = getCyberDateTime();
                float ctr = (GraphicsDevice.Viewport.Width / 2);
                float btm = GraphicsDevice.Viewport.Height;
                float rght = (GraphicsDevice.Viewport.Width) - (Content.Load<SpriteFont>("Fonts/font").MeasureString(str).X);
                RenderText(str, new Vector2(rght, TextLineSpacing), Color.Lime);

                // MY TODO LIST
                //int t = 0;
                //foreach (string todo in DevelopmentTODOList)
                //{
                //    RenderText(todo, new Vector2(8, (TextLineSpacing * 2) + (TextLineSpacing * t)), Color.Red, -1, "fonts/fontSmall");
                //    t++;
                //}

                // HUD Buttons
                int hbcnt = HUDButtons.Count - 1;
                for (; hbcnt >= 0; hbcnt--)
                    HUDButtons[hbcnt].Draw(gameTime);

                RenderText(string.Format("Sector: {0}", CurrentSector.Name), new Vector2(4, TextLineSpacing), Color.SkyBlue);

                spriteBatch.End();

                GraphicsDevice.SetRenderTarget(null);

                GraphicsDevice.SetRenderTarget(fireEffect);
                GraphicsDevice.Clear(Color.Black);

                Fire2.Oxygen = .999f;
                Fire2.Draw(this, gameTime);

                GraphicsDevice.SetRenderTarget(null);

                // Re render scene
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
                spriteBatch.Draw(Scene, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White);
                spriteBatch.End();
            }

            // Draw PopupMsgBox
            RenderPopupMessageBoxs(gameTime);
            
            spriteBatch.Begin();

            if (OptionsScreen)
            {
                Vector2 origin = new Vector2(ptrSize, ptrSize) / 2;

                if (loading || saving)
                {
                    loadRot += .1f;

                    spriteBatch.Draw(Content.Load<Texture2D>("Textures/PtrOptionsSpinner"), new Rectangle(ptrPos.X - 4, ptrPos.Y + 4, ptrSize, ptrSize), null, Color.Black, loadRot, origin * 2, SpriteEffects.None, 0);
                    
                    if(loading)
                        spriteBatch.Draw(Content.Load<Texture2D>("Textures/PtrOptionsSpinner"), new Rectangle(ptrPos.X, ptrPos.Y, ptrSize, ptrSize), null, Color.LimeGreen, loadRot, origin * 2, SpriteEffects.None, 0);
                    else
                        spriteBatch.Draw(Content.Load<Texture2D>("Textures/PtrOptionsSpinner"), new Rectangle(ptrPos.X, ptrPos.Y, ptrSize, ptrSize), null, Color.Purple, loadRot, origin * 2, SpriteEffects.None, 0);
                }
                else
                    spriteBatch.Draw(Content.Load<Texture2D>("Textures/PtrOptions"), new Rectangle(ptrPos.X - (int)origin.X, ptrPos.Y - (int)origin.Y, ptrSize, ptrSize), Color.Silver);
            }
            else
                spriteBatch.Draw(Content.Load<Texture2D>("Textures/Ptr"), new Rectangle(ptrPos.X, ptrPos.Y, ptrSize, ptrSize), Color.SkyBlue);

            spriteBatch.End();
        }

        public void ShowPopUpMessage(string title, string message, int width, int height, Color color, Color titleGBColor, Color titleColor, Vector2 popupOffset, float fadeSpeed = .05f, int durationSeconds = 5)
        {
            if (PopUps.SingleOrDefault(entity => entity.PopUpTitle == title && entity.PopUpMessage == message) == null)
                PopUps.Add(new UIPopupMessageBox(this, title, message, width, height, color, titleGBColor, titleColor, popupOffset, fadeSpeed) { msgBoxPopupDuration = new TimeSpan(0, 0, 0, durationSeconds, 0) });
        }
        public void ShowPopUpMessage(string title, string message, int width, int height, Color color, Color titleGBColor, Color titleColor, float fadeSpeed = .05f, int durationSeconds = 5)
        {
            Say(string.Format("{0}. {1}", title, message));
            ShowPopUpMessage(title, message, width, height, color, titleGBColor, titleColor, Vector2.Zero, fadeSpeed, durationSeconds);
        }

        public void ShowErrorMessage(string title, string message, int width, int height, float fadeSpeed = .05f)
        {
            ShowPopUpMessage(title, message, width, height, Color.White, Color.Red, Color.Gold,fadeSpeed);
        }

        void RenderPopupMessageBoxs(GameTime gameTime)
        {
            int popCnt = PopUps.Count-1;

            for (; popCnt >= 0; popCnt--)
            {
                PopUps[popCnt].Draw(gameTime);

                if (PopUps[popCnt].FadedOut)
                    PopUps.Remove(PopUps[popCnt]);
            }
        }

        void SaveJPG(string name, Texture2D testure)
        {

        }

        public int ptrSize = 32;

        public string getCyberDate()
        {
            return string.Format("[{0:0000}/{1:00}]", Year, Cycle);
        }

        public string getLastCyberDate()
        {
            int year = Year;
            int cycle = Cycle-1;

            if (cycle <= 0)
            {
                year--;
                cycle = 12;
            }

            return string.Format("[{0:0000}/{1:00}]", year, cycle);
        }

        public string getCyberDateTime()
        {
            return string.Format("Date: [{0:0000}/{1:00}] Next Cycle: [{2:hh\\:mm\\:ss\\.fff}]", Year, Cycle, NextCycle);
        }

        Vector2 newsPos = new Vector2(0, 0);

        public void DrawMesh(Model model, Matrix wrld, string technique, Color color, float opacity = .75f, Texture2D texture = null)
        {
            if (effect == null)
            {
                effect = Content.Load<Effect>("Shaders/NodeShader");
            }

            if (technique == "DoubleSideTextured")
                effect.Parameters["material"].SetValue(texture);

            effect.CurrentTechnique = effect.Techniques[technique];

            Matrix wvp = wrld * camera.View * camera.Projection;

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    effect.Parameters["wvp"].SetValue(wvp);
                    effect.Parameters["Color"].SetValue(color.ToVector4());

                    effect.Parameters["world"].SetValue(wrld);
                    effect.Parameters["Opacity"].SetValue(opacity);

                    effect.CurrentTechnique.Passes[0].Apply();

                    GraphicsDevice.SetVertexBuffer(part.VertexBuffer);
                    GraphicsDevice.Indices = part.IndexBuffer;
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.VertexOffset, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
                }
            }
        }

        public void DrawBubble(Rectangle location, Color color, Vector2 pegPos, int pegSize = 0)
        {
            spriteBatch.Draw(Content.Load<Texture2D>("Textures/Bubble"), location, color);

            if (pegPos != Vector2.Zero)
            {
                Rectangle pegRect = new Rectangle();
                float rotation = 0;
                pegPos.Normalize();
                // Above
                if (pegPos == new Vector2(0, -1))
                {
                    if (pegSize == 0)
                        pegSize = location.Width / 8;
                    pegRect = new Rectangle(location.Left + location.Width / 2, location.Top, pegSize, pegSize);
                    rotation = MathHelper.Pi;
                }
                // Bellow
                if (pegPos == new Vector2(0, 1))
                {
                    if (pegSize == 0)
                        pegSize = location.Width / 8;
                    pegRect = new Rectangle(location.Left + location.Width / 2, location.Bottom, pegSize, pegSize);
                }
                // Left 
                if (pegPos == new Vector2(-1, 0))
                {
                    pegRect = new Rectangle(location.Left, location.Top + location.Height / 2, location.Height / 4, location.Height / 2);
                    rotation = MathHelper.PiOver2;
                }
                // Right
                if (pegPos == new Vector2(1, 0))
                {
                    pegRect = new Rectangle(location.Right, location.Top + location.Height / 2, location.Height / 4, location.Height / 2);
                    rotation = -MathHelper.PiOver2;
                }

                spriteBatch.Draw(Content.Load<Texture2D>("Textures/BubblePeg"), pegRect, null, color, rotation, Vector2.Zero, SpriteEffects.None, 0);
            }
        }

        #region Utilities

        public static Vector2 Get2DCoords(Vector3 myPosition, Camera Camera)
        {
            Matrix ViewProjectionMatrix = Camera.View * Camera.Projection;

            Vector4 result4 = Vector4.Transform(myPosition, ViewProjectionMatrix);

            if (result4.W <= 0)
                return new Vector2(Camera.Viewport.Width * 2, 0);

            Vector3 result = new Vector3(result4.X / result4.W, result4.Y / result4.W, result4.Z / result4.W);

            Vector2 retVal = new Vector2((int)Math.Round(+result.X * (Camera.Viewport.Width / 2)) + (Camera.Viewport.Width / 2), (int)Math.Round(-result.Y * (Camera.Viewport.Height / 2)) + (Camera.Viewport.Height / 2));
            return retVal;
        }

        public static Ray BuildRay(Point screenPixel, Camera camera)
        {
            Vector3 nearSource = camera.Viewport.Unproject(new Vector3(screenPixel.X, screenPixel.Y, camera.Viewport.MinDepth), camera.Projection, camera.View, Matrix.Identity);
            Vector3 farSource = camera.Viewport.Unproject(new Vector3(screenPixel.X, screenPixel.Y, camera.Viewport.MaxDepth), camera.Projection, camera.View, Matrix.Identity);
            Vector3 direction = farSource - nearSource;

            direction.Normalize();

            return new Ray(nearSource, direction);
        }

        public static float RayPicking(Point screenPixel, BoundingSphere volume, Camera camera)
        {
            Nullable<float> retVal = float.MaxValue;

            BuildRay(screenPixel, camera).Intersects(ref volume, out retVal);

            if (retVal != null)
                return retVal.Value;
            else
                return float.MaxValue;
        }

        public static float RayPicking(Point screenPixel, BoundingBox volume, Camera camera)
        {
            Nullable<float> retVal = float.MaxValue;

            BuildRay(screenPixel, camera).Intersects(ref volume, out retVal);

            if (retVal != null)
                return retVal.Value;
            else
                return float.MaxValue;
        }

        public static float TurnToFace(Vector2 position, Vector2 faceThis,
            float currentAngle, float turnSpeed, float offset)
        {
            // consider this diagram:
            //         C 
            //        /|
            //      /  |
            //    /    | y
            //  / o    |
            // S--------
            //     x
            // 
            // where S is the position of the spot light, C is the position of the cat,
            // and "o" is the angle that the spot light should be facing in order to 
            // point at the cat. we need to know what o is. using trig, we know that
            //      tan(theta)       = opposite / adjacent
            //      tan(o)           = y / x
            // if we take the arctan of both sides of this equation...
            //      arctan( tan(o) ) = arctan( y / x )
            //      o                = arctan( y / x )
            // so, we can use x and y to find o, our "desiredAngle."
            // x and y are just the differences in position between the two objects.
            float x = faceThis.X - position.X;
            float y = faceThis.Y - position.Y;

            // we'll use the Atan2 function. Atan will calculates the arc tangent of 
            // y / x for us, and has the added benefit that it will use the signs of x
            // and y to determine what cartesian quadrant to put the result in.
            // http://msdn2.microsoft.com/en-us/library/system.math.atan2.aspx
            float desiredAngle = (float)Math.Atan2(y, x) + offset;

            // so now we know where we WANT to be facing, and where we ARE facing...
            // if we weren't constrained by turnSpeed, this would be easy: we'd just 
            // return desiredAngle.
            // instead, we have to calculate how much we WANT to turn, and then make
            // sure that's not more than turnSpeed.

            // first, figure out how much we want to turn, using WrapAngle to get our
            // result from -Pi to Pi ( -180 degrees to 180 degrees )
            float difference = WrapAngle(desiredAngle - currentAngle);

            // clamp that between -turnSpeed and turnSpeed.
            difference = MathHelper.Clamp(difference, -turnSpeed, turnSpeed);

            // so, the closest we can get to our target is currentAngle + difference.
            // return that, using WrapAngle again.
            return WrapAngle(currentAngle + difference);
        }

        public static float WrapAngle(float radians)
        {
            while (radians < -MathHelper.Pi)
            {
                radians += MathHelper.TwoPi;
            }
            while (radians > MathHelper.Pi)
            {
                radians -= MathHelper.TwoPi;
            }
            return radians;
        }

        public static Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, bool clipSide, Matrix projectionMatrix)
        {
            planeNormalDirection.Normalize();
            Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
            if (clipSide)
                planeCoeffs *= -1;

            Matrix worldViewProjection = currentViewMatrix * projectionMatrix;
            Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
            inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
            Plane finalPlane = new Plane(planeCoeffs);

            return finalPlane;
        }

        public static Quaternion LookAt(Vector3 target, float speed, Vector3 position, Quaternion rotation, Vector3 fwd)
        {
            LookAt(target, speed, position, ref rotation, fwd);

            return rotation;
        }
        public static void LookAt(Vector3 target, float speed, Vector3 position, ref Quaternion rotation, Vector3 fwd)
        {
            if (fwd == Vector3.Zero)
                fwd = Vector3.Forward;

            Vector3 tminusp = target - position;
            Vector3 ominusp = fwd;

            if (tminusp == Vector3.Zero)
                return;

            tminusp.Normalize();

            float theta = (float)System.Math.Acos(Vector3.Dot(tminusp, ominusp));
            Vector3 cross = Vector3.Cross(ominusp, tminusp);

            if (cross == Vector3.Zero)
                return;

            cross.Normalize();

            Quaternion targetQ = Quaternion.CreateFromAxisAngle(cross, theta);
            rotation = Quaternion.Slerp(rotation, targetQ, speed);
        }

        public void DrawFireWall(FireWallData fwd)
        {
            List<Vector3> positions = new List<Vector3>();
            List<Matrix> rotations = new List<Matrix>();

            Dictionary<int, float> drawOrder = new Dictionary<int, float>();
           
            
            for (int side = 0; side < 4; side++)
            {
                switch (side)
                {
                    case 0: // Back
                        positions.Add(fwd.Position + new Vector3(0, fwd.Scale.Y, -fwd.Scale.X));
                        rotations.Add(Matrix.Identity);
                        break;
                    case 1: // Right
                        positions.Add(fwd.Position + new Vector3(fwd.Scale.X, fwd.Scale.Y, 0));
                        rotations.Add(Matrix.CreateRotationY(MathHelper.PiOver2));
                        break;
                    case 2: // Left
                        positions.Add(fwd.Position + new Vector3(-fwd.Scale.X, fwd.Scale.Y, 0));
                        rotations.Add(Matrix.CreateRotationY(-MathHelper.PiOver2));
                        break;
                    case 3: // Front
                        positions.Add(fwd.Position + new Vector3(0, fwd.Scale.Y, fwd.Scale.X));
                        rotations.Add(Matrix.Identity);
                        break;
                }
                drawOrder.Add(side, Vector3.Distance(positions[positions.Count - 1], camera.Position));
            }

            drawOrder = drawOrder.OrderByDescending(entity => entity.Value).ToDictionary(x => x.Key, x => x.Value);

            DepthStencilState ds = GraphicsDevice.DepthStencilState;
            BlendState bs = GraphicsDevice.BlendState;

            GraphicsDevice.BlendState = BlendState.AlphaBlend;
            GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            
            foreach(int idx in drawOrder.Keys)
                DrawMesh(Content.Load<Model>("Models/panel"), Matrix.CreateScale(fwd.Scale) * rotations[idx] * Matrix.CreateTranslation(positions[idx]), "DoubleSideTextured", fwd.Color, fwd.Opacity, fireEffect);

            GraphicsDevice.BlendState = bs;
            GraphicsDevice.DepthStencilState = ds;
        }       

        public static Quaternion LookAtLockRotation(Vector3 target, float speed, Vector3 position, Quaternion rotation, Vector3 fwd, Vector3 lockedRots)
        {
            LookAt(target, speed, position, ref rotation, fwd);

            LockRotation(ref rotation, lockedRots);

            return rotation;
        }

        public static void LockRotation(ref Quaternion rotation, Vector3 lockedRots)
        {
            lockedRots -= -Vector3.One;
            Vector3 rots = QuaternionToEulerAngleVector3(rotation) * lockedRots;
            rotation = Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationX(rots.X) * Matrix.CreateRotationY(rots.Y) * Matrix.CreateRotationX(rots.Z));
        }

        public static Vector3 QuaternionToEulerAngleVector3(Quaternion rotation)
        {
            Vector3 rotationaxes = new Vector3();
            Vector3 forward = Vector3.Transform(Vector3.Forward, rotation);
            Vector3 up = Vector3.Transform(Vector3.Up, rotation);

            rotationaxes = AngleTo(new Vector3(), forward);

            if (rotationaxes.X == MathHelper.PiOver2)
            {
                rotationaxes.Y = (float)Math.Atan2((double)up.X, (double)up.Z);
                rotationaxes.Z = 0;
            }
            else if (rotationaxes.X == -MathHelper.PiOver2)
            {
                rotationaxes.Y = (float)Math.Atan2((double)-up.X, (double)-up.Z);
                rotationaxes.Z = 0;
            }
            else
            {
                up = Vector3.Transform(up, Matrix.CreateRotationY(-rotationaxes.Y));
                up = Vector3.Transform(up, Matrix.CreateRotationX(-rotationaxes.X));

                //rotationaxes.Z = (float)Math.Atan2((double)-up.Z, (double)up.Y);
                rotationaxes.Z = (float)Math.Atan2((double)-up.X, (double)up.Y);
            }

            return rotationaxes;
        }

        // Returns Euler angles that point from one point to another
        public static Vector3 AngleTo(Vector3 from, Vector3 location)
        {
            Vector3 angle = new Vector3();
            Vector3 v3 = Vector3.Normalize(location - from);

            angle.X = (float)Math.Asin(v3.Y);
            angle.Y = (float)Math.Atan2((double)-v3.X, (double)-v3.Z);

            return angle;
        }

        #endregion
        const string OptionsFile = "Options.opt";
        public void SaveOptions()
        {
            try
            {
                IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
                IsolatedStorageFileStream isfs = isf.CreateFile(OptionsFile);
                StreamWriter sw = new StreamWriter(isfs);

                sw.WriteLine(AudioOn);
                sw.WriteLine(MusicOn);
                sw.WriteLine(SFXOn);
                sw.WriteLine(SpeechOn);
                sw.WriteLine(TutorialOn);
                sw.WriteLine(MusicVolume);

                sw.Close();
            }
            catch { }
        }
       
        public void LoadOptions()
        {
            try
            {
                IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);

                if (isf.FileExists(OptionsFile))
                {
                    IsolatedStorageFileStream isfs = isf.OpenFile(OptionsFile, FileMode.Open, FileAccess.Read);
                    StreamReader sr = new StreamReader(isfs);

                    AudioOn = bool.Parse(sr.ReadLine());
                    MusicOn = bool.Parse(sr.ReadLine());
                    SFXOn = bool.Parse(sr.ReadLine());
                    SpeechOn = bool.Parse(sr.ReadLine());
                    TutorialOn = bool.Parse(sr.ReadLine());

                    MusicVolume = float.Parse(sr.ReadLine());

                    sr.Close();
                }
            }
            catch { }
        }

        bool loading = false;
        bool saving = false;

        long mem = 0;

        void saveGameThread(object name)
        {
            GameData.SavedTimeOfDay = DateTime.Now.TimeOfDay;

            try
            {
                using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null))
                {
                    using (IsolatedStorageFileStream isfs = isf.CreateFile(name.ToString()))
                    {
                        using (StreamWriter sw = new StreamWriter(isfs))
                        {
                            sw.Write(Convert.ToBase64String(SerializableBase<GameData>.Serialize(GameData)));
                            sw.Close();
                        }
                    }
                }

                ShowPopUpMessage("Game Saved", string.Format("Game {0} saved successfully...", name), 336, 128, Color.White, Color.Green, Color.Gold);
            }
            catch(Exception e)
            {
                ShowErrorMessage("Game Save Failed", string.Format("Failed to save game {0}.\n{1}", name,e.Message), 336, 128);
            }

            saving = false;
            loadRot = 0;
            
        }

        Thread loadThread;
        Thread saveThread;
        // C:\Users\CH2\AppData\Local\IsolatedStorage\z5zvkx5r.tgs\a05esfoz.x5z\Url.sa5m4ek1miivrysnawmymy3502wr4o3v\Url.sa5m4ek1miivrysnawmymy3502wr4o3v\Files
        public void SaveGame(string name)
        {
            saving = true;
            Say(string.Format("Saving game to file {0}", name));
            (saveThread = new Thread(new ParameterizedThreadStart(saveGameThread))).Start(name);
        }

        public void SelectedLoadGame(UIZone sender)
        {
            if (loadFilesLst.SelectedItem != null)
                LoadGame(loadFilesLst.SelectedItem.Name);
            else
                ShowErrorMessage("Bad Load File", "You have not selected a file to load.", 600, 70);
        }

        public void LoadGame(string name)
        {
            loading = true;
            Say(string.Format("Loading game file {0}", name));
            (loadThread = new Thread(new ParameterizedThreadStart(loadGameThread))).Start(name);
        }

        public void loadGameThread(object name)
        {
            try
            {
                using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null))
                {

                    if (isf.FileExists(OptionsFile))
                    {
                        using (IsolatedStorageFileStream isfs = isf.OpenFile(name.ToString(), FileMode.Open, FileAccess.Read))
                        {
                            using (StreamReader sr = new StreamReader(isfs))
                            {
                                GameData.UnWire();

                                SerializableBase<GameData>.Deserialize(Convert.FromBase64String(sr.ReadToEnd()), ref GameData);

                                // Time offsets
                                // Set the Cycle start in line with the current time.
                                GameData.CycleStart = DateTime.Now.TimeOfDay - (GameData.SavedTimeOfDay - GameData.CycleStart);

                                // Do th same for share recording
                                GameData.lastRecordedSharePrice = DateTime.Now.TimeOfDay - (GameData.SavedTimeOfDay - GameData.lastRecordedSharePrice);

                                GameData.ReWire(this, ButtonClicked);

                                sr.Close();
                            }
                        }

                        ShowPopUpMessage("Game Loaded", string.Format("Game {0} loaded successfully...", name), 336, 128, Color.White, Color.Green, Color.Gold);
                    }
                    else
                        ShowErrorMessage("Game Load Failed", string.Format("Could not find file {0}.", name), 336, 128);
                }
            }
            catch(Exception e)
            {
                ShowErrorMessage("Game Load Failed", string.Format("Failed to load game {0}.\n{1}", name,e.Message), 336, 128);
            }

            loading = false;
            loadRot = 0;
        }

        protected override void EndRun()
        {
            // Close threads
            if (saveThread != null && saving)
                saveThread.Join(0);

            if (loadThread != null && loading)
                loadThread.Abort();

            SaveOptions();
            base.EndRun();
        }

        public string[] DevelopmentTODOList = new string[] 
        { 
            " DEV TODO LIST",
            "---------------",
            "Sort out share timer on re load",
            "Finish off Rewire calls to nodes",
            "Code up node screens/actions",
            "Finish news engine" ,
            "Make news engine more dynamic" ,
            "Smooth transitions",
            "Implement hacking system.",
            "---- EOL ----",
        };

        public static float RoundToMultipleOf(float toRound, float multiple)
        {
            return (float)RoundToMultipleOf((int)toRound, (int)multiple);
        }
        public static int RoundToMultipleOf(int toRound, int multiple)
        {
            if (multiple == 0)
                return toRound;

            return ((toRound + multiple - 1) / multiple) * multiple;
        }

        
        public void Say(string say, bool asysc = false, string voice = "")
        {
            if (asysc)
                SayAsync(say, voice);
            else
            {
                if (AudioOn && SpeechOn)
                {
                    if (voice != string.Empty)
                    {
                        // Is the voice valid?
                        if (Speechsynth.GetInstalledVoices().SingleOrDefault(v => v.VoiceInfo.Name == voice && v.Enabled) != null)
                            Speechsynth.SelectVoice(voice);
                    }

                    Speechsynth.SpeakAsync(say);
                }
            }
        }

        public void SayAsync(string say, string voice = "")
        {
            if (AudioOn && SpeechOn)
            {
                SpeechSynthesizer thisSpeechsynth = new SpeechSynthesizer();
                if (voice != string.Empty)
                {
                    // Is the voice valid?
                    if (thisSpeechsynth.GetInstalledVoices().SingleOrDefault(v => v.VoiceInfo.Name == voice && v.Enabled) != null)
                        thisSpeechsynth.SelectVoice(voice);
                }

                thisSpeechsynth.SpeakAsync(say);
            }
        }
    }
}
